package com.panopset.refactorsf;

import static com.panopset.Util.dspmsg;
import static com.panopset.Util.getCanonicalPath;
import static com.panopset.Util.log;
import static com.panopset.Util.x;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;

import com.panopset.UpdateListener;
import com.panopset.java.Version;
import com.panopset.sf.Windoc;

/**
 * Super class of PanFile and PanDirectory.
 * 
 * @author Karl Dinwiddie
 * 
 */
public abstract class AbstractPanFile extends PanField<JPanel> {

	@Override
	protected String getGuiValue() {
		return getFileTextField().getText();
	}

	@Override
	protected void setGuiValue(String value) {
		getFileTextField().setText(value);
	}

	/**
	 * 
	 * @param borderTitle
	 *            Border title.
	 * @param toolTipText
	 *            Tool tip text.
	 * @param defaultValue
	 *            Default value.
	 */
	public AbstractPanFile(final Windoc wd, final String borderTitle,
			final String toolTipText, final String defaultValue,
			final String key) {
		super(wd, borderTitle, toolTipText, defaultValue, key);
		getPanel().setLayout(new GridLayout(1, 1));
	}

	/**
	 * Description.
	 */
	private String description = "";

	/**
	 * Update listeners.
	 */
	private final List<UpdateListener> updateListeners = new ArrayList<UpdateListener>();

	/**
	 * Add update listener.
	 * 
	 * @param updateListener
	 *            Update listener.
	 */
	public final void addUpdateListener(final UpdateListener updateListener) {
		updateListeners.add(updateListener);
	}

	/**
	 * Update all listeners.
	 */
	private void update() {
		for (UpdateListener updateListener : updateListeners) {
			updateListener.update();
		}
	}

	/**
	 * Set selected file.
	 * 
	 * @param file
	 *            Selected file.
	 */
	public final void setSelectedFile(final File file) {
		if (file != null && file.exists()) {
			getFileTextField().setText(getCanonicalPath(file));
			update();
		}
	}

	/**
	 * Get file.
	 * 
	 * @return File.
	 */
	public final File getFile() {
		return new File(getGuiValue());
	}

	/**
	 * Create a file or directory path.
	 */
	protected abstract void create();

	/**
	 * Create new file.
	 */
	final void doNew() {
		setSelectedFile(fileDialog(JFileChooser.SAVE_DIALOG));
		File f = getFile();
		if (f == null || "".equals(getCanonicalPath(f))) {
			dspmsg(x("No file selected."));
			return;
		}
		create();
	}

	/**
	 * @param dialogType
	 *            JFileChooser Dialog type.
	 * @return Selected file.
	 */
	private File fileDialog(final int dialogType) {
		File rtn = null;
		File dftPath = new File(getCanonicalPath(new File("x")))
				.getParentFile();
		JFileChooser chooser = new JFileChooser();
		if ("".equals(getFileTextField().getText().trim())) {
			if ((dftPath != null) && (getCanonicalPath(dftPath).length() > 0)) {
				chooser.setSelectedFile(dftPath);
			}
		} else {
			chooser.setSelectedFile(getFile());
		}
		chooser.setFileSelectionMode(getFileSelectionMode());
		chooser.setDialogTitle(getPrompt());
		chooser.setDialogType(dialogType);
		if (chooser.showOpenDialog(new JFrame()) == JFileChooser.APPROVE_OPTION) {
			rtn = chooser.getSelectedFile();
		}
		if (rtn != null) {
			getFileTextField().setText(getCanonicalPath(rtn));
		}
		update();
		return rtn;
	}

	/**
	 * @return JFileDialog selection mode.
	 */
	protected abstract int getFileSelectionMode();

	/**
	 * @return PanFile panel.
	 */
	public final JPanel createGui() {
		return getPanel();
	}

	/**
	 * Create component.
	 */
	private TPanel panel;

	@Override
	public final TPanel createComponent() {
		String requiredPrompt = null;

		requiredPrompt = createPrompter() + " " + x("required");

		panel = new TPanel.Builder().tip(x("Select") + " " + description)
				.layout(new GridBagLayout()).requiredPrompt(requiredPrompt)
				.construct();

		final GridBagConstraints gbc = new GridBagConstraints();

		TButton btn = new TButton(x("Browse"), KeyEvent.VK_B, x("Select") + " "
				+ description, new ActionListener() {

			public void actionPerformed(final ActionEvent e) {
				doNew();
			}
		});
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 0;
		panel.add(btn, gbc);
		gbc.gridx++;
		panel.add(getDropper(), gbc);
		gbc.gridx++;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.weightx = 1.0;
		panel.add(getFileTextField(), gbc);
		return panel;
	}

	/**
	 * dropper.
	 */
	private TTextField dropper;

	/**
	 * @return Dropper.
	 */
	private TTextField getDropper() {
		if (dropper == null) {
			dropper = new TTextField(2);
			dropper.setEditable(false);
			dropper.setText(">>");
			dropper.setToolTipText(x("Drag 'n drop file here."));
			dropper.setBackground(Color.CYAN);
			dropper.setDragEnabled(true);
			dropper.setTransferHandler(new FileDropHandler());
		}
		return dropper;
	}

	/**
	 * File text field.
	 */
	private TTextField fileTextField;

	/**
	 * @return File text field.
	 */
	public TTextField getFileTextField() {
		if (fileTextField == null) {
			fileTextField = new TTextField(FILENAME_WIDTH);
		}
		return fileTextField;
	}

	/**
	 * 30.
	 */
	private static final int FILENAME_WIDTH = 30;

	/**
	 * File drop handler.
	 * 
	 * Reference:
	 * 
	 * http://weblogs.java.net/blog/shan_man/archive/2006/09/toplevel_drop_w.
	 * html
	 * 
	 */
	final class FileDropHandler extends TransferHandler {

		/**
		 * Can import.
		 * 
		 * @param supp
		 *            Transfer support.
		 * @return True if the type of drag and drop is a list of files.
		 */
		public boolean canImport(final TransferSupport supp) {
			/* for the demo, we'll only support drops (not clipboard paste) */
			if (!supp.isDrop()) {
				return false;
			}

			/* return true if and only if the drop contains a list of files */
			return supp.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
		}

		/**
		 * Import data.
		 * 
		 * @param supp
		 *            Transfer support.
		 * @return True if successful.
		 */
		public boolean importData(final TransferSupport supp) {
			if (!canImport(supp)) {
				return false;
			}

			/* fetch the Transferable */
			Transferable t = supp.getTransferable();

			try {
				Object data = t.getTransferData(DataFlavor.javaFileListFlavor);
				if (!(data instanceof java.util.List<?>)) {
					return false;
				}
				java.util.List<?> objList = (java.util.List<?>) data;
				for (Object o : objList) {
					if (o instanceof File) {
						File file = (File) o;
						AbstractPanFile.this.setSelectedFile(file);
						return true;
					}
				}
			} catch (Exception e) {
				log(e);
			}

			return false;
		}

		/**
		 * Serial version id.
		 */
		private static final long serialVersionUID = Version.UNI_VERSION;
	}

	/**
	 * Prompt.
	 */
	private String prompt;

	/**
	 * @return Prompt.
	 */
	private String getPrompt() {
		if (prompt == null) {
			prompt = x("Please select a") + " " + createPrompter();
		}
		return prompt;
	}

	/**
	 * @return Canonical path of file.
	 */
	public final String getFullPath() {
		return getCanonicalPath(getFile());
	}

	/**
	 * @return directory or file prompter.
	 */
	protected abstract String createPrompter();
}
